Controller的責任可大可小,不過最基本的部份大概有:
使用controller,除了讓應用程式的結構更清楚,也可以把一些標準處理與流程結合進去,減少開發的功夫。
Controller的責任可大可小,不過最基本的部份大概有:
使用controller,除了讓應用程式的結構更清楚,也可以把一些標準處理與流程結合進去,減少開發的功夫。
很多Framework的Controller,都會使用front-controller的方式來實作,也就是說,所有的request實際上都是丟給一支程式(index.php),然後他再根據規則剖析URL,決定要使用哪個controller的哪個方法,來處理目前的request。
系統通常會用這樣的方式組織起來:
例如"index.php"不帶任何參數,也許就是呼叫Index controller的index方法,然後功能是論壇的列表;"index.php?mod=Forum&id=1"就會呼叫Forum controller的index方法,帶入參數id=1,顯示論壇id=1的文章列表;"index.php?mod=Forum&func=edit&id=2"就會呼叫Forum controller的edit方法,帶入參數id=2,顯示編輯論壇id=1的一些資料的介面等等。
所以...如果系統的參數如果沒有一定的規則,第一步並不是使用controller,而是修改系統,讓參數遵守一定的規則,這樣才有辦法用一致的方式來組織程式。
不管三七二十一,先實作一個簡單的controller吧。
<?php
namespace Fillano\Core;
class Controller
{
public static function dispatch()
{
//import input vars
$get = array();
foreach($_GET as $k => $v) {
$get[$k] = (is_numeric($v))? (is_int($v))? intval($v):floatval($v):(is_string($v))? filter_var($v, FILTER_SANITIZE_STRING):$v;
}
$post = array();
foreach($_POST as $k => $v) {
$post[$k] = (is_numeric($v))? (is_int($v))? intval($v):floatval($v):(is_string($v))? filter_var($v, FILTER_SANITIZE_STRING):$v;
}
//start dispatching
if(isset($get['mod'])) {
$class = "Fillano\\Controllers\\".strval($get['mod']);
} else {
$class = "Fillano\\Controllers\\Index"
}
if(isset($_GET['func'])) {
$method = strval($get['func']);
}
else {
$method = 'Index';
}
//search and dispatching
if(class_exists($class)) {
$request = new Request($get, $post);
$response = new Response(TEMPLATE_ENGINE, 'views');
$controller = new $class;
if(method_exists($controller, $method)) {
call_user_method_array($method, $controller, array($request, $response));
} else {
$controller->index($request, $response);
}
} else {
header('HTTP/1.1 404 Not Found');
}
}
public function index($request, $response)
{
}
public function useModel($name)
{
return \Fillano\Core\ModelFactory::getInstance(MODEL_IMPL, $name);
}
}
所有的controller都要繼承這個Controller類別,透過方法的參數接收Request與Response物件,Request->get是過濾與跳脫過的GET參數,Request->post是過濾與跳脫過的POST參數。Response物件提供一個userView方法,可以取得指定的view物件。Controller->getModel()方法,則可以取得指定的model物件。就先這樣做,先不管設計好不好...
Reqeust類別很簡單:
<?php
namespace Fillano\Core;
class Request
{
public $get;
public $post;
public function __construct($get, $post)
{
$this->get = $get;
$this->post = $post;
}
}
Response類別也很簡單:
<?php
namespace Fillano\Core;
class Response
{
private $engine;
private $path;
public function __construct($engine='Twig', $path='views')
{
$this->engine = $engine;
$this->path = $path;
}
public function useView() {
return \Fillano\Core\View::getInstance($this->engine, $this->path);
}
}
簡單地說,就是把輸入集中在Request物件,輸出集中在Response物件,不過目前的設計是急就章
接下來寫controller,把他們集中在class/Fillano/Controllers目錄,也就是Fillano\Controllers這個namespace中。
先針對首頁來做,預設的類別是Index,預設的方法是index,所以在Fillano\Controllers中增加一個類別Index,繼承Controller。寫法很簡單,就把原先在index.php中除了bootstrap之外的程式碼,先搬到Index->index方法中,然後根據剛剛設計的Controller運作方式做一下調整:
<?php
namespace Fillano\Controllers;
use Fillano\Core\Controller;
class Index extends Controller
{
public function index($req, $rep)
{
if(isset($_SESSION['user']['account'])) {
$member = true;
$name = $_SESSION['user']['name'];
} else $member = false;
if(isset($_SESSION['msg'])) {
$message = mysql_real_escape_string($_SESSION['msg']);
unset($_SESSION['msg']);
}
//$model = new Fillano\Models\MysqlIndex($conn);
$model = $this->useModel('Index');
$data = $model->getForumList();
$view = $rep->useView();
$view->assign(array(
'member'=>$member,
'data'=>$data,
'name'=>$name,
'message'=>$message
));
$view->render('index', 'html');
}
}
實際上程式只改了兩行,就是取得model與view的這兩行。
至於原本的index.php呢?現在長這樣:
<?php
require 'bootstrap.php';
Fillano\Core\Controller::dispatch();
現在只剩兩行,一行引入bootstrap.php,一行做dispatch...功能只剩下Front Controller了。
這個簡單controller只做了四件事情:
這樣寫的好處是:原本的程式轉移到controller非常快速,也幾乎不會出錯。不過當然功能還有非常多欠缺,也需要規劃更好的架構,讓其他需要的功能,可以有彈性地載入進來。
另外,其實已經有不少的開放原始碼的Router架構,適合用來作Controller,不妨上Github找找。
======
明天是最後一天了,先把目前的架構套到列出論壇文章的forum.php上,然後做一個簡單的總結吧。